package info.batcloud.fanli.core.service.impl;

import com.alibaba.fastjson.JSON;
import info.batcloud.fanli.core.constants.CacheNameConstants;
import info.batcloud.fanli.core.entity.UserSetting;
import info.batcloud.fanli.core.event.SettingChangeEvent;
import info.batcloud.fanli.core.repository.UserSettingRepository;
import info.batcloud.fanli.core.service.UserSettingService;
import info.batcloud.fanli.core.settings.DdkAuthSetting;
import info.batcloud.fanli.core.settings.DdkSetting;
import info.batcloud.fanli.core.settings.JdUnionSetting;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@CacheConfig(cacheNames = CacheNameConstants.SYSTEM_SETTING)
public class UserSettingServiceImpl implements UserSettingService {

    private final Map<String, Class> SETTING_CLASS_MAP = new HashMap<>();

    @Inject
    private UserSettingRepository userSettingRepository;

    @Inject
    private ApplicationEventPublisher eventPublisher;

    @PostConstruct
    public void init() {
        register(DdkAuthSetting.class);
        register(JdUnionSetting.class);
        register(DdkSetting.class);
    }

    private void register(Class setting) {
        String key = setting.getSimpleName();

        if (SETTING_CLASS_MAP.containsKey(key)) {
            throw new RuntimeException("setting key is exists, key:" + key);
        }
        SETTING_CLASS_MAP.put(key, setting);
    }

    @Override
    public long saveSetting(long userId, Object setting, Date expireTime) {
        return saveSetting(userId, setting.getClass(), JSON.toJSONString(setting), expireTime);
    }

    @Override
    public long saveSetting(long userId, String key, String content, Date expireTime) {
        return saveSetting(userId, SETTING_CLASS_MAP.get(key), content, expireTime);
    }

    @Override
    public long saveSetting(long userId, Class clazz, String content, Date expireTime) {
        String key = clazz.getSimpleName();
        UserSetting userSetting = userSettingRepository.findByKeyAndUserId(key, userId);
        if (userSetting == null) {
            userSetting = new UserSetting();
            userSetting.setKey(key);
            userSetting.setUserId(userId);
        }
        userSetting.setExpireTime(expireTime);
        userSetting.setContent(content);
        userSettingRepository.save(userSetting);
        eventPublisher.publishEvent(new SettingChangeEvent(clazz));
        return userSetting.getId();
    }

    @Override
    public <T> T findSetting(long userId, Class<T> clazz) {
        String key = clazz.getSimpleName();
        UserSetting userSetting = userSettingRepository.findByKeyAndUserId(key, userId);
        return parseSetting(clazz, userSetting);
    }

    @Override
    public <T> T findSetting(long userId, String key) {
        return findSetting(userId, (Class<T>) SETTING_CLASS_MAP.get(key));
    }

    private <T> T parseSetting(Class<T> clazz, UserSetting userSetting) {
        if (userSetting == null) {
            try {
                return (T) Class.forName(clazz.getName()).newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return JSON.parseObject(userSetting.getContent(), clazz);
    }

    @Override
    public void deleteSetting(long userId, String key) {
        deleteSetting(userId, SETTING_CLASS_MAP.get(key));
    }

    @Override
    public void deleteSetting(long userId, Class clazz) {
        UserSetting userSetting = userSettingRepository.findByKeyAndUserId(clazz.getSimpleName(), userId);
        userSettingRepository.delete(userSetting.getId());
    }

    @Override
    public <T> List<T> findList(String key) {
        return findList(SETTING_CLASS_MAP.get(key));
    }

    @Override
    public <T> List<T> findList(Class<T> clazz) {
        List<UserSetting> settings = userSettingRepository.findByKey(clazz.getSimpleName());
        return settings.stream().map(setting -> parseSetting(clazz, setting)).collect(Collectors.toList());
    }

}
